<!DOCTYPE html>
<html lang="en" dir="auto">

<head><meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="index, follow">
<title>Mathematics In 3DGS 2 | WangJV Blog</title>
<meta name="keywords" content="3D Gaussian Splatting, 体渲染, 计算机图形学, 数学">
<meta name="description" content="1. 矩阵求导的常用方法
1.1. 矩阵求导的一般方法
在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：

向量对标量求导
向量对向量求导
向量对矩阵求导
矩阵对标量求导
矩阵对向量求导
矩阵对矩阵求导

事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。
1.1.1. 矩阵对标量求导
假设我们有矩阵 $\mathbf A$ 和标量 $k$，其中矩阵 $\mathbf A$ 的展开形式为：
$$
\mathbf A=  
\left[
\begin{matrix}
a_{11}&  a_{12}&  \cdots& \ a_{1n} \\
a_{21}&  a_{22}&  \cdots& \ a_{2n} \\
\vdots&  \vdots&  \ddots&  \vdots \\
a_{n1}&  a_{n2}&  \cdots& \ a_{nn} \\
\end{matrix}
\right]
$$那么，$\frac{d \mathbf A}{d k}$被定义为：
$$
\frac{d \mathbf A}{d k} =
\left[
\begin{matrix}
\frac{d a_{11}}{d k}&  \frac{d a_{12}}{d k}&   \cdots& \ \frac{d a_{1n}}{d k}&  \\
\frac{d a_{21}}{d k}&  \frac{d a_{22}}{d k}&   \cdots& \ \frac{d a_{2n}}{d k}&  \\
\vdots&  \vdots&  \ddots&  \vdots \\
\frac{d a_{n1}}{d k}&  \frac{d a_{n2}}{d k}&   \cdots& \ \frac{d a_{nn}}{d k}&  \\
\end{matrix}
\right]
$$于上述定义类似，如果标量对矩阵求导，即$\frac{d k}{d \mathbf A}$，其定义为：">
<meta name="author" content="WangJV">
<link rel="canonical" href="https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-2/">
<link crossorigin="anonymous" href="https://wangjv0812.github.io/WangJV-Blog-Pages/assets/css/stylesheet.8fe10233a706bc87f2e08b3cf97b8bd4c0a80f10675a143675d59212121037c0.css" integrity="sha256-j&#43;ECM6cGvIfy4Is8&#43;XuL1MCoDxBnWhQ2ddWSEhIQN8A=" rel="preload stylesheet" as="style">
<link rel="icon" href="https://wangjv0812.github.io/WangJV-Blog-Pages/favicon.ico">
<link rel="icon" type="image/png" sizes="16x16" href="https://wangjv0812.github.io/WangJV-Blog-Pages/favicon-16x16.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://wangjv0812.github.io/WangJV-Blog-Pages/favicon-32x32.png">
<link rel="apple-touch-icon" href="https://wangjv0812.github.io/WangJV-Blog-Pages/apple-touch-icon.png">
<link rel="mask-icon" href="https://wangjv0812.github.io/WangJV-Blog-Pages/safari-pinned-tab.svg">
<meta name="theme-color" content="#2e2e33">
<meta name="msapplication-TileColor" content="#2e2e33">
<link rel="alternate" hreflang="en" href="https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-2/">
<noscript>
    <style>
        #theme-toggle,
        .top-link {
            display: none;
        }

    </style>
    <style>
        @media (prefers-color-scheme: dark) {
            :root {
                --theme: rgb(29, 30, 32);
                --entry: rgb(46, 46, 51);
                --primary: rgb(218, 218, 219);
                --secondary: rgb(155, 156, 157);
                --tertiary: rgb(65, 66, 68);
                --content: rgb(196, 196, 197);
                --code-block-bg: rgb(46, 46, 51);
                --code-bg: rgb(55, 56, 62);
                --border: rgb(51, 51, 51);
            }

            .list {
                background: var(--theme);
            }

            .list:not(.dark)::-webkit-scrollbar-track {
                background: 0 0;
            }

            .list:not(.dark)::-webkit-scrollbar-thumb {
                border-color: var(--theme);
            }
        }

    </style>
</noscript><script>
  MathJax = {
    tex: {
      displayMath: [['\\[', '\\]'], ['$$', '$$']],
      inlineMath: [['\\(', '\\)'], ['$', '$']],
      processEscapes: true,
      processEnvironments: true,
      tags: 'ams'
    },
    chtml: {
      scale: 1,                     
      minScale: 0.5,               
      matchFontHeight: false,       
      displayAlign: 'center',       
      displayIndent: '0',           
      mtextInheritFont: false,      
      merrorInheritFont: true,      
      mathmlSpacing: false,         
      skipHtmlTags: ['script','noscript','style','textarea','pre','code','a'],
      ignoreHtmlClass: 'tex2jax_ignore',
      processHtmlClass: 'tex2jax_process'
    },
    svg: {
      scale: 1,                     
      minScale: 0.5,               
      mtextInheritFont: false,      
      merrorInheritFont: true,
      mathmlSpacing: false,
      skipHtmlTags: ['script','noscript','style','textarea','pre','code','a'],
      ignoreHtmlClass: 'tex2jax_ignore',
      processHtmlClass: 'tex2jax_process'
    },
    options: {
      enableMenu: true,             
      menuOptions: {
        settings: {
          zoom: 'Click'             
        }
      }
    },
    loader: {
      load: ['ui/safe', 'a11y/assistive-mml']
    },
    startup: {
      ready() {
        MathJax.startup.defaultReady();
        
        const observer = new ResizeObserver(entries => {
          MathJax.typesetPromise();
        });
        observer.observe(document.body);
      }
    }
  };
  
  
  if (window.innerWidth <= 768) {
    MathJax.chtml = MathJax.chtml || {};
    MathJax.chtml.scale = 0.9;  
  }
</script>
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>


<style>
   
  .MathJax {
    outline: 0;
  }
  
   
  @media (max-width: 768px) {
    .MathJax {
      font-size: 90% !important;
    }
    
     
    .MathJax_Display {
      overflow-x: auto;
      overflow-y: hidden;
      padding: 0 !important;
      margin: 1em 0 !important;
    }
    
     
    .MathJax_CHTML {
      line-height: 1.2 !important;
    }
  }
  
   
  mjx-container[jax="CHTML"][display="true"] {
    overflow-x: auto;
    overflow-y: hidden;
    padding: 1px 0;
  }
</style><meta property="og:url" content="https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-2/">
  <meta property="og:site_name" content="WangJV Blog">
  <meta property="og:title" content="Mathematics In 3DGS 2">
  <meta property="og:description" content="1. 矩阵求导的常用方法 1.1. 矩阵求导的一般方法 在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：
向量对标量求导 向量对向量求导 向量对矩阵求导 矩阵对标量求导 矩阵对向量求导 矩阵对矩阵求导 事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。
1.1.1. 矩阵对标量求导 假设我们有矩阵 $\mathbf A$ 和标量 $k$，其中矩阵 $\mathbf A$ 的展开形式为：
$$\mathbf A= \left[\begin{matrix}a_{11}&amp; a_{12}&amp; \cdots&amp; \ a_{1n} \\a_{21}&amp; a_{22}&amp; \cdots&amp; \ a_{2n} \\\vdots&amp; \vdots&amp; \ddots&amp; \vdots \\a_{n1}&amp; a_{n2}&amp; \cdots&amp; \ a_{nn} \\\end{matrix}\right]$$那么，$\frac{d \mathbf A}{d k}$被定义为：
$$\frac{d \mathbf A}{d k} =\left[\begin{matrix}\frac{d a_{11}}{d k}&amp; \frac{d a_{12}}{d k}&amp; \cdots&amp; \ \frac{d a_{1n}}{d k}&amp; \\\frac{d a_{21}}{d k}&amp; \frac{d a_{22}}{d k}&amp; \cdots&amp; \ \frac{d a_{2n}}{d k}&amp; \\\vdots&amp; \vdots&amp; \ddots&amp; \vdots \\\frac{d a_{n1}}{d k}&amp; \frac{d a_{n2}}{d k}&amp; \cdots&amp; \ \frac{d a_{nn}}{d k}&amp; \\\end{matrix}\right]$$于上述定义类似，如果标量对矩阵求导，即$\frac{d k}{d \mathbf A}$，其定义为：">
  <meta property="og:locale" content="en-us">
  <meta property="og:type" content="article">
    <meta property="article:section" content="posts">
    <meta property="article:published_time" content="2024-05-17T17:13:56+08:00">
    <meta property="article:modified_time" content="2024-05-17T17:13:56+08:00">
    <meta property="article:tag" content="3D Gaussian Splatting">
    <meta property="article:tag" content="体渲染">
    <meta property="article:tag" content="计算机图形学">
    <meta property="article:tag" content="数学">
      <meta property="og:image" content="https://wangjv0812.github.io/WangJV-Blog-Pages/">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://wangjv0812.github.io/WangJV-Blog-Pages/">
<meta name="twitter:title" content="Mathematics In 3DGS 2">
<meta name="twitter:description" content="1. 矩阵求导的常用方法
1.1. 矩阵求导的一般方法
在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：

向量对标量求导
向量对向量求导
向量对矩阵求导
矩阵对标量求导
矩阵对向量求导
矩阵对矩阵求导

事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。
1.1.1. 矩阵对标量求导
假设我们有矩阵 $\mathbf A$ 和标量 $k$，其中矩阵 $\mathbf A$ 的展开形式为：
$$
\mathbf A=  
\left[
\begin{matrix}
a_{11}&  a_{12}&  \cdots& \ a_{1n} \\
a_{21}&  a_{22}&  \cdots& \ a_{2n} \\
\vdots&  \vdots&  \ddots&  \vdots \\
a_{n1}&  a_{n2}&  \cdots& \ a_{nn} \\
\end{matrix}
\right]
$$那么，$\frac{d \mathbf A}{d k}$被定义为：
$$
\frac{d \mathbf A}{d k} =
\left[
\begin{matrix}
\frac{d a_{11}}{d k}&  \frac{d a_{12}}{d k}&   \cdots& \ \frac{d a_{1n}}{d k}&  \\
\frac{d a_{21}}{d k}&  \frac{d a_{22}}{d k}&   \cdots& \ \frac{d a_{2n}}{d k}&  \\
\vdots&  \vdots&  \ddots&  \vdots \\
\frac{d a_{n1}}{d k}&  \frac{d a_{n2}}{d k}&   \cdots& \ \frac{d a_{nn}}{d k}&  \\
\end{matrix}
\right]
$$于上述定义类似，如果标量对矩阵求导，即$\frac{d k}{d \mathbf A}$，其定义为：">


<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "position":  1 ,
      "name": "Posts",
      "item": "https://wangjv0812.github.io/WangJV-Blog-Pages/posts/"
    }, 
    {
      "@type": "ListItem",
      "position":  2 ,
      "name": "Mathematics In 3DGS 2",
      "item": "https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-2/"
    }
  ]
}
</script>
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "BlogPosting",
  "headline": "Mathematics In 3DGS 2",
  "name": "Mathematics In 3DGS 2",
  "description": "1. 矩阵求导的常用方法 1.1. 矩阵求导的一般方法 在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：\n向量对标量求导 向量对向量求导 向量对矩阵求导 矩阵对标量求导 矩阵对向量求导 矩阵对矩阵求导 事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。\n1.1.1. 矩阵对标量求导 假设我们有矩阵 $\\mathbf A$ 和标量 $k$，其中矩阵 $\\mathbf A$ 的展开形式为：\n$$\r\\mathbf A= \\left[\r\\begin{matrix}\ra_{11}\u0026 a_{12}\u0026 \\cdots\u0026 \\ a_{1n} \\\\\ra_{21}\u0026 a_{22}\u0026 \\cdots\u0026 \\ a_{2n} \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\ra_{n1}\u0026 a_{n2}\u0026 \\cdots\u0026 \\ a_{nn} \\\\\r\\end{matrix}\r\\right]\r$$那么，$\\frac{d \\mathbf A}{d k}$被定义为：\n$$\r\\frac{d \\mathbf A}{d k} =\r\\left[\r\\begin{matrix}\r\\frac{d a_{11}}{d k}\u0026 \\frac{d a_{12}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{1n}}{d k}\u0026 \\\\\r\\frac{d a_{21}}{d k}\u0026 \\frac{d a_{22}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{2n}}{d k}\u0026 \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\r\\frac{d a_{n1}}{d k}\u0026 \\frac{d a_{n2}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{nn}}{d k}\u0026 \\\\\r\\end{matrix}\r\\right]\r$$于上述定义类似，如果标量对矩阵求导，即$\\frac{d k}{d \\mathbf A}$，其定义为：\n",
  "keywords": [
    "3D Gaussian Splatting", "体渲染", "计算机图形学", "数学"
  ],
  "articleBody": "1. 矩阵求导的常用方法 1.1. 矩阵求导的一般方法 在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：\n向量对标量求导 向量对向量求导 向量对矩阵求导 矩阵对标量求导 矩阵对向量求导 矩阵对矩阵求导 事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。\n1.1.1. 矩阵对标量求导 假设我们有矩阵 $\\mathbf A$ 和标量 $k$，其中矩阵 $\\mathbf A$ 的展开形式为：\n$$\r\\mathbf A= \\left[\r\\begin{matrix}\ra_{11}\u0026 a_{12}\u0026 \\cdots\u0026 \\ a_{1n} \\\\\ra_{21}\u0026 a_{22}\u0026 \\cdots\u0026 \\ a_{2n} \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\ra_{n1}\u0026 a_{n2}\u0026 \\cdots\u0026 \\ a_{nn} \\\\\r\\end{matrix}\r\\right]\r$$那么，$\\frac{d \\mathbf A}{d k}$被定义为：\n$$\r\\frac{d \\mathbf A}{d k} =\r\\left[\r\\begin{matrix}\r\\frac{d a_{11}}{d k}\u0026 \\frac{d a_{12}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{1n}}{d k}\u0026 \\\\\r\\frac{d a_{21}}{d k}\u0026 \\frac{d a_{22}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{2n}}{d k}\u0026 \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\r\\frac{d a_{n1}}{d k}\u0026 \\frac{d a_{n2}}{d k}\u0026 \\cdots\u0026 \\ \\frac{d a_{nn}}{d k}\u0026 \\\\\r\\end{matrix}\r\\right]\r$$于上述定义类似，如果标量对矩阵求导，即$\\frac{d k}{d \\mathbf A}$，其定义为：\n$$\r\\frac{d k}{d \\mathbf A} =\r\\left[\r\\begin{matrix}\r\\frac{d k}{d a_{11}}\u0026 \\frac{d k}{d a_{12}}\u0026 \\cdots\u0026 \\ \\frac{d k}{d a_{1n}}\u0026 \\\\\r\\frac{d k}{d a_{21}}\u0026 \\frac{d k}{d a_{22}}\u0026 \\cdots\u0026 \\ \\frac{d k}{d a_{2n}}\u0026 \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\r\\frac{d k}{d a_{n1}}\u0026 \\frac{d k}{d a_{n2}}\u0026 \\cdots\u0026 \\ \\frac{d k}{d a_{nn}}\u0026 \\\\\r\\end{matrix}\r\\right]\r$$1.1.2. 矩阵对矩阵求导 矩阵对矩阵求导是更加复杂的矩阵对标量的求导，假设我们有矩阵 $\\mathbf A, \\mathbf B$，有：\n$$\r\\mathbf A= \\left[\r\\begin{matrix}\ra_{11}\u0026 a_{12}\u0026 \\cdots\u0026 \\ a_{1n} \\\\\ra_{21}\u0026 a_{22}\u0026 \\cdots\u0026 \\ a_{2n} \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\ra_{n1}\u0026 a_{n2}\u0026 \\cdots\u0026 \\ a_{nn} \\\\\r\\end{matrix}\r\\right]\r$$那么有：\n$$\r\\frac{d\\mathbf A}{d\\mathbf B} =\r\\left[\r\\begin{matrix}\r\\frac{d a_{11}}{d \\mathbf B}\u0026 \\frac{d a_{12}}{d \\mathbf B}\u0026 \\cdots\u0026 \\ \\frac{d a_{1n}}{d \\mathbf B}\u0026 \\\\\r\\frac{d a_{21}}{d \\mathbf B}\u0026 \\frac{d a_{22}}{d \\mathbf B}\u0026 \\cdots\u0026 \\ \\frac{d a_{2n}}{d \\mathbf B}\u0026 \\\\\r\\vdots\u0026 \\vdots\u0026 \\ddots\u0026 \\vdots \\\\\r\\frac{d a_{n1}}{d \\mathbf B}\u0026 \\frac{d a_{n2}}{d \\mathbf B}\u0026 \\cdots\u0026 \\ \\frac{d a_{nn}}{d \\mathbf B}\u0026 \\\\\r\\end{matrix}\r\\right]\r$$之后套用矩阵与标量之间的微分运算的定义即可计算。但是不难发现，这样需要将整个矩阵展开来求导过于繁琐负责了，也破坏了原本矩阵作为一个整体所蕴含的性质。我们需要一些更加简洁的方法来实现矩阵求导。\n1.2. 矩阵求导的全微分方法 在具体的优化问题中，我们面对的往往是一个多多变量的复杂计算过程。这要求我们处理多变量，多步骤的矩阵多项式函数计算微分。假设我们一个计算链的输入是 $s_i$，输出是 $s_o$，计算链中的某一步计算是：\n$$\rC = f(A, B)\r$$1.2.1. 正向传播的计算 那么显而易见的，其全微分为：\n$$\rdC = \\frac{\\partial f}{\\partial A} dA + \\frac{\\partial f}{\\partial B} dB\r$$当输入，即 $s_i$ 上一个扰动时（记作 $\\dot{S_i}$），在 $C$ 上的扰动为：\n$$\r\\dot{C} = \\frac{\\partial f}{\\partial A} \\dot A + \\frac{\\partial f}{\\partial B} \\dot B\r$$1.2.2. 反向传播的计算 我们分析从运算链中的某一步骤向输出传到的过程：\n$$\rs_0 = \\sum C_{ij}\r$$那么，有：\n$$\rds_o = \\sum \\frac{\\partial s_0}{\\partial C_{ij}} dC_{ij} = Tr(\\frac{\\partial s_0}{\\partial C} ^T dC)\r$$带入计算我们之前给出的计算链 $C = f(A, B)$，有：\n$$\r\\begin{aligned}\rd(S_0) \u0026= Tr(C^T dC)\\\\\rdC \u0026= \\frac{\\partial f}{\\partial A} dA + \\frac{\\partial f}{\\partial B} dB\r\\end{aligned}\r$$根据迹的分配律，有：\n$$\r\\begin{aligned}\rd(S_0)\r\u0026= Tr(C^T, dC) \\\\\r\u0026= Tr(C^T, \\frac{\\partial f}{\\partial A}dA) + Tr(C^T, \\frac{\\partial f}{\\partial B} dB)\\\\\r\u0026= Tr(\\frac{\\partial f}{\\partial A}^TC, dA) + Tr(\\frac{\\partial f}{\\partial B}^T C, dB)\\\\\r\u0026= Tr(\\frac{\\partial s_o}{\\partial A}, dA) + Tr(\\frac{\\partial s_o}{\\partial B}, dB)\r\\end{aligned}\r$$这样我们可以知道：\n$$\r\\begin{aligned}\r\\frac{\\partial s_o}{\\partial A} = \\frac{\\partial f}{\\partial A}^TC \\\\\r\\frac{\\partial s_o}{\\partial B} = \\frac{\\partial f}{\\partial B}^T C\r\\end{aligned}\r$$1.2.3. Frobenius内积 为了更好的表达运算关系，我们定义一个二元运算 $\\langle \\cdot , \\cdot \\rangle$。这个二元组定义的本质在于矩阵的微分运算是对其中每个元素进行的，不能简单套用对矩阵全体定义的矩阵乘法运算。\n$$\r\\langle X, Y\\rangle = Tr(X^T Y) = Vec(X)^T Vec(Y) \\in \\mathbb R\r$$其性质有：\n$$\r\\begin{aligned}\r\\langle X,Y\\rangle \u0026 =\\langle Y,X\\rangle, \\\\\r\\langle X,Y\\rangle \u0026 =\\langle X^\\top,Y^\\top\\rangle, \\\\\r\\langle X,YZ\\rangle \u0026 =\\langle Y^{\\top}X,Z\\rangle=\\langle XZ^{\\top},Y\\rangle, \\\\\r\\langle X,Y+Z\\rangle \u0026 =\\langle X,Y\\rangle+\\langle X,Z\\rangle.\r\\end{aligned}\r$$用这个二元运算重新叙述上面的推导过程，可能会更加清晰。假设我们有一个函数 $f(X), X\\in \\mathbb R^{m\\times n}$，并且 $X = AY, where. A\\in \\mathbb R^{m\\times p}, Y\\in \\mathbb R^{p\\times n}$，\n我们可以把 $f$ 写作任意标量 $x$ 的梯度： $$\r\\frac{\\partial f}{\\partial x} = \\langle\\frac{\\partial f}{\\partial X}, \\frac{\\partial X}{\\partial x} \\rangle\r$$我们有：\n$$\r\\partial f = \\langle \\frac{\\partial f}{\\partial X}, \\partial X \\rangle\r$$并定义：$G = \\frac{\\partial f}{\\partial X}$，有：\n$$\r\\begin{aligned}\r\\frac{\\partial f}{\\partial x}\u0026 =\\langle G,\\frac{\\partial(AY)}{\\partial x}\\rangle \\\\\r\u0026=\\langle G,\\frac{\\partial A}{\\partial x}Y\\rangle+\\langle G,A\\frac{\\partial Y}{\\partial x}\\rangle \\\\\r\u0026=\\langle GY^{\\top},\\frac{\\partial A}{\\partial x}\\rangle+\\langle A^{\\top}G,\\frac{\\partial Y}{\\partial x}\\rangle.\r\\end{aligned}\r$$对比，可归纳出：\n$$\r\\frac{\\partial f}{\\partial A}=GY^\\top\\in\\mathbb{R}^{m\\times p},\\quad\\frac{\\partial f}{\\partial Y}=A^\\top G\\in\\mathbb{R}^{p\\times n}.\r$$1.3. 常见矩阵运算的求导 1.3.1. 加法 假设有 $C = A+B$，那么有：\n$$\rdC = dA + dB\r$$且有：\n$$\r\\begin{aligned}\rds_o\r\u0026= \\langle \\frac {\\partial s_o}{\\partial C} , dC\\rangle\\\\\r\u0026= \\langle \\frac {\\partial s_o}{\\partial C} , dA\\rangle + \\langle \\frac {\\partial s_o}{\\partial C} , dB\\rangle\\\\\r\u0026= \\langle \\frac {\\partial s_o}{\\partial A} , dA\\rangle + \\langle \\frac {\\partial s_o}{\\partial B} , dB\\rangle\\\\\r\\end{aligned}\r$$那么：\n$$\r\\begin{aligned}\r\\frac{\\partial s_o}{\\partial A} = \\frac{\\partial s_o}{\\partial C}\\\\\r\\frac{\\partial s_o}{\\partial B} = \\frac{\\partial s_o}{\\partial C}\r\\end{aligned}\r$$1.3.2. 乘法 我们假设有：$C=AB$，那么：\n$$\rdC = dA\\; B + A\\; dB\r$$类似的，有：\n$$\r\\begin{aligned}\rds_o\r\u0026= \\langle \\frac{ds_o}{dC}, dC \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dC}, dA\\; B \\rangle + \\langle \\frac{ds_o}{dC}, A\\; dB\\rangle \\\\\r\u0026= \\langle \\frac{ds_o}{dC} B^T, dA \\rangle + \\langle A^T \\frac{ds_o}{dC}, dB\\rangle \\\\\r\u0026= \\langle \\frac{\\partial s_o}{\\partial A}, dA\\rangle + \\langle \\frac{\\partial s_o}{\\partial B}, dB\\rangle\r\\end{aligned}\r$$那么，我们可以归纳出:\n$$\r\\begin{aligned}\r\\frac{\\partial s_o}{\\partial A} \u0026= \\frac{ds_o}{dC} B^T \\\\\r\\frac{\\partial s_o}{\\partial B} \u0026= A^T \\frac{ds_o}{dC}\r\\end{aligned}\r$$1.3.3. 求逆 假设我们有：$C = A^{-1}$，可推得：\n$$\r\\begin{aligned}\r\u0026CA = I \\\\\r\\Longrightarrow \u0026 dC\\; A + C\\; dA = 0\\\\\r\\Longrightarrow \u0026 dC = -C dA C \\\\\r\\end{aligned}\r$$那么有正向传播：\n$$\r\\frac{d C}{d s_i} = -C \\frac{d A}{d s_i} C\r$$类似的，反向传播有：\n$$\r\\begin{aligned}\rds_o\r\u0026= \\langle \\frac{ds_o}{dC}, dC \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dC}, -C dA C \\rangle\\\\\r\u0026= \\langle -C^{T} \\frac{ds_o}{dC} C^T , dA\\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dA} , dA\\rangle\r\\end{aligned}\r$$那么有：\n$$\r\\frac{ds_o}{dA} = -C^{T} \\frac{ds_o}{dC} C^T\r$$1.3.4. 二次型 假设：$C= x^T A x$，那么有：\n$$\rdC = x^TA\\, dx + d(x^T A)x\r$$应为二次型结果是标量，那么 $d(x^T A)x$ 也应是标量，则有$(d(x^T A)x)^T = d(x^T A)x$，则有：\n$$\r\\begin{aligned}\rdC\r\u0026=x^{T}Adx+x^{T}d\\left(A^{T}x\\right) \\\\\r\u0026=x^{T}Adx+x^{T}A^{T}dx \\\\\r\u0026=x^{T}\\left(A+A^{T}\\right)dx\r\\end{aligned}\r$$类似的，反向传播结果为：\n$$\r\\begin{aligned}\rds_o\r\u0026= \\langle \\frac{ds_o}{dC}, dC \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dC}, x^{T}(A+A^{T})dx \\rangle\\\\\r\u0026= \\langle (A+A^{T})x\\frac{ds_o}{dC}, dx \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dx}, dx \\rangle\\\\\r\\end{aligned}\r$$那么有：\n$$\r\\frac{ds_o}{dx} = (A+A^{T})x\\frac{ds_o}{dC}\r$$1.3.5. 行列式 有：$C=det\\, A$。有 $A$ 的伴随矩阵 $\\tilde A$，伴随矩阵的定义为元素 $A_{ij}$ 的代数余子式，具体形式为：\n$$\r\\tilde A_{ij} = (-1)^{i+j}\\det(\\widehat{A}_{ij})\r$$根据行列式的定义，有：\n$$\r\\det A=\\sum_jA_{i,j}\\tilde{A}_{i,j}.\r$$我们固定 $i$，很容易可以得到：\n$$\r\\frac{\\partial C}{\\partial A_{i,j}}=\\tilde{A}_{i,j}\r$$可推知：\n$$\rdC = \\sum_{ij} \\tilde A_{ij} dA_ij\r$$将矩阵除法的定义 $A^{-1}=(\\det A)^{-1}\\tilde{A}^T$ 带入上式中，则有：\n$$\r\\begin{aligned}\rdC\r\u0026= \\sum_{ij} \\tilde A_{ij} dA_{ij}\\\\\r\u0026= \\sum_{ij} A^{-1}_{ij} \\det A dA_{ij}\\\\\r\u0026= \\det A\\sum_{ij} A^{-1}_{ij} dA_{ij}\\\\\r\u0026= C\\, Tr(A^{-1} dA)\\\\\r\\end{aligned}\r$$反向梯度有：\n$$\r\\begin{aligned}\rds_o\r\u0026= \\langle \\frac{ds_o}{dC}, dC \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dC}, C\\, Tr(A^{-1} dA) \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dC} C A^{-T}, dA \\rangle\\\\\r\u0026= \\langle \\frac{ds_o}{dA}, dA \\rangle\\\\\r\\end{aligned}\r$$那么有：\n$$\r\\frac{ds_o}{dA} = \\frac{ds_o}{dC} C A^{-T}\r$$2. 3DGS 中光栅化的正向和反向过程 2.1. 深度合成过程 2.1.1. 正向过程 此处的正向过程是指已经投影在二维像素平面上的二维高斯函数合成的过程。我们不加说明的给出合成的形式，具体推导可以参考上一篇文章。\n$$\r\\begin{aligned}\rC_i = \\sum_{n\\leq N} c_n \\cdot \\alpha_n \\cdot T_n\\\\\r\\text{where. } T_n = \\prod_{m",
  "wordCount" : "958",
  "inLanguage": "en",
  "image": "https://wangjv0812.github.io/WangJV-Blog-Pages/","datePublished": "2024-05-17T17:13:56+08:00",
  "dateModified": "2024-05-17T17:13:56+08:00",
  "author":{
    "@type": "Person",
    "name": "WangJV"
  },
  "mainEntityOfPage": {
    "@type": "WebPage",
    "@id": "https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-2/"
  },
  "publisher": {
    "@type": "Organization",
    "name": "WangJV Blog",
    "logo": {
      "@type": "ImageObject",
      "url": "https://wangjv0812.github.io/WangJV-Blog-Pages/favicon.ico"
    }
  }
}
</script>
</head>

<body class="" id="top">
<script>
    if (localStorage.getItem("pref-theme") === "dark") {
        document.body.classList.add('dark');
    } else if (localStorage.getItem("pref-theme") === "light") {
        document.body.classList.remove('dark')
    } else if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        document.body.classList.add('dark');
    }

</script>

<header class="header">
    <nav class="nav">
        <div class="logo">
            <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/" accesskey="h" title="WangJV Blog (Alt + H)">WangJV Blog</a>
            <div class="logo-switches">
                <button id="theme-toggle" accesskey="t" title="(Alt + T)" aria-label="Toggle theme">
                    <svg id="moon" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
                    </svg>
                    <svg id="sun" xmlns="http://www.w3.org/2000/svg" width="24" height="18" viewBox="0 0 24 24"
                        fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
                        stroke-linejoin="round">
                        <circle cx="12" cy="12" r="5"></circle>
                        <line x1="12" y1="1" x2="12" y2="3"></line>
                        <line x1="12" y1="21" x2="12" y2="23"></line>
                        <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
                        <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
                        <line x1="1" y1="12" x2="3" y2="12"></line>
                        <line x1="21" y1="12" x2="23" y2="12"></line>
                        <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
                        <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
                    </svg>
                </button>
            </div>
        </div>
        <ul id="menu">
            <li>
                <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/" title="Home">
                    <span>Home</span>
                </a>
            </li>
            <li>
                <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/posts/" title="Posts">
                    <span>Posts</span>
                </a>
            </li>
            <li>
                <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/archives/" title="Archive">
                    <span>Archive</span>
                </a>
            </li>
            <li>
                <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/tags/" title="Tags">
                    <span>Tags</span>
                </a>
            </li>
            <li>
                <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/search/" title="🔍 Search (Alt &#43; /)" accesskey=/>
                    <span>🔍 Search</span>
                </a>
            </li>
        </ul>
    </nav>
</header>
<main class="main">

<article class="post-single">
  <header class="post-header">
    <div class="breadcrumbs"><a href="https://wangjv0812.github.io/WangJV-Blog-Pages/">Home</a>&nbsp;»&nbsp;<a href="https://wangjv0812.github.io/WangJV-Blog-Pages/posts/">Posts</a></div>
    <h1 class="post-title entry-hint-parent">
      Mathematics In 3DGS 2
    </h1>
    <div class="post-meta"><span title='2024-05-17 17:13:56 +0800 CST'>May 17, 2024</span>&nbsp;·&nbsp;5 min&nbsp;·&nbsp;958 words&nbsp;·&nbsp;WangJV&nbsp;|&nbsp;<a href="https://github.com/WangJV0812/WangJV-Blog-Source/tree/master/content/posts/Mathematics_In_3DGS_2/inedx.md" rel="noopener noreferrer edit" target="_blank">Suggest Changes</a>

</div>
  </header> <div class="toc">
    <details >
        <summary accesskey="c" title="(Alt + C)">
            <span class="details">Table of Contents</span>
        </summary>

        <div class="inner"><nav id="TableOfContents">
  <ul>
    <li><a href="#1-矩阵求导的常用方法">1. 矩阵求导的常用方法</a>
      <ul>
        <li><a href="#11-矩阵求导的一般方法">1.1. 矩阵求导的一般方法</a></li>
        <li><a href="#12-矩阵求导的全微分方法">1.2. 矩阵求导的全微分方法</a></li>
        <li><a href="#13-常见矩阵运算的求导">1.3. 常见矩阵运算的求导</a></li>
      </ul>
    </li>
    <li><a href="#2-3dgs-中光栅化的正向和反向过程">2. 3DGS 中光栅化的正向和反向过程</a>
      <ul>
        <li><a href="#21-深度合成过程">2.1. 深度合成过程</a></li>
        <li><a href="#22-投影过程">2.2. 投影过程</a></li>
      </ul>
    </li>
  </ul>
</nav>
        </div>
    </details>
</div>

  <div class="post-content"><h2 id="1-矩阵求导的常用方法">1. 矩阵求导的常用方法<a hidden class="anchor" aria-hidden="true" href="#1-矩阵求导的常用方法">#</a></h2>
<h3 id="11-矩阵求导的一般方法">1.1. 矩阵求导的一般方法<a hidden class="anchor" aria-hidden="true" href="#11-矩阵求导的一般方法">#</a></h3>
<p>在矩阵论的课程中，我们学习过如下几种分析相关的知识，分别是：</p>
<ol>
<li>向量对标量求导</li>
<li>向量对向量求导</li>
<li>向量对矩阵求导</li>
<li>矩阵对标量求导</li>
<li>矩阵对向量求导</li>
<li>矩阵对矩阵求导</li>
</ol>
<p>事实上不难发现，我们只需要搞明白了矩阵对标量求导和矩阵对矩阵求导的方法，其他问题均可从这个两个原则推理开去。因此我们叙述的重点放在这两个问题上。</p>
<h4 id="111-矩阵对标量求导">1.1.1. 矩阵对标量求导<a hidden class="anchor" aria-hidden="true" href="#111-矩阵对标量求导">#</a></h4>
<p>假设我们有矩阵 $\mathbf A$ 和标量 $k$，其中矩阵 $\mathbf A$ 的展开形式为：</p>
$$
\mathbf A=  
\left[
\begin{matrix}
a_{11}&  a_{12}&  \cdots& \ a_{1n} \\
a_{21}&  a_{22}&  \cdots& \ a_{2n} \\
\vdots&  \vdots&  \ddots&  \vdots \\
a_{n1}&  a_{n2}&  \cdots& \ a_{nn} \\
\end{matrix}
\right]
$$<p>那么，$\frac{d \mathbf A}{d k}$被定义为：</p>
$$
\frac{d \mathbf A}{d k} =
\left[
\begin{matrix}
\frac{d a_{11}}{d k}&  \frac{d a_{12}}{d k}&   \cdots& \ \frac{d a_{1n}}{d k}&  \\
\frac{d a_{21}}{d k}&  \frac{d a_{22}}{d k}&   \cdots& \ \frac{d a_{2n}}{d k}&  \\
\vdots&  \vdots&  \ddots&  \vdots \\
\frac{d a_{n1}}{d k}&  \frac{d a_{n2}}{d k}&   \cdots& \ \frac{d a_{nn}}{d k}&  \\
\end{matrix}
\right]
$$<p>于上述定义类似，如果标量对矩阵求导，即$\frac{d k}{d \mathbf A}$，其定义为：</p>
$$
\frac{d k}{d \mathbf A} =
\left[
\begin{matrix}
\frac{d k}{d a_{11}}&  \frac{d k}{d a_{12}}&   \cdots& \ \frac{d k}{d a_{1n}}&  \\
\frac{d k}{d a_{21}}&  \frac{d k}{d a_{22}}&   \cdots& \ \frac{d k}{d a_{2n}}&  \\
\vdots&  \vdots&  \ddots&  \vdots \\
\frac{d k}{d a_{n1}}&  \frac{d k}{d a_{n2}}&   \cdots& \ \frac{d k}{d a_{nn}}&  \\
\end{matrix}
\right]
$$<h4 id="112-矩阵对矩阵求导">1.1.2. 矩阵对矩阵求导<a hidden class="anchor" aria-hidden="true" href="#112-矩阵对矩阵求导">#</a></h4>
<p>矩阵对矩阵求导是更加复杂的矩阵对标量的求导，假设我们有矩阵 $\mathbf A, \mathbf B$，有：</p>
$$
\mathbf A=  
\left[
\begin{matrix}
a_{11}&  a_{12}&  \cdots& \ a_{1n} \\
a_{21}&  a_{22}&  \cdots& \ a_{2n} \\
\vdots&  \vdots&  \ddots&  \vdots \\
a_{n1}&  a_{n2}&  \cdots& \ a_{nn} \\
\end{matrix}
\right]
$$<p>那么有：</p>
$$
\frac{d\mathbf A}{d\mathbf B} =
\left[
\begin{matrix}
\frac{d a_{11}}{d \mathbf B}&  \frac{d a_{12}}{d \mathbf B}&   \cdots& \ \frac{d a_{1n}}{d \mathbf B}&  \\
\frac{d a_{21}}{d \mathbf B}&  \frac{d a_{22}}{d \mathbf B}&   \cdots& \ \frac{d a_{2n}}{d \mathbf B}&  \\
\vdots&  \vdots&  \ddots&  \vdots \\
\frac{d a_{n1}}{d \mathbf B}&  \frac{d a_{n2}}{d \mathbf B}&   \cdots& \ \frac{d a_{nn}}{d \mathbf B}&  \\
\end{matrix}
\right]
$$<p>之后套用矩阵与标量之间的微分运算的定义即可计算。但是不难发现，这样需要将整个矩阵展开来求导过于繁琐负责了，也破坏了原本矩阵作为一个整体所蕴含的性质。我们需要一些更加简洁的方法来实现矩阵求导。</p>
<h3 id="12-矩阵求导的全微分方法">1.2. 矩阵求导的全微分方法<a hidden class="anchor" aria-hidden="true" href="#12-矩阵求导的全微分方法">#</a></h3>
<p>在具体的优化问题中，我们面对的往往是一个多多变量的复杂计算过程。这要求我们处理多变量，多步骤的矩阵多项式函数计算微分。假设我们一个计算链的输入是 $s_i$，输出是 $s_o$，计算链中的某一步计算是：</p>
$$
C = f(A, B)
$$<h4 id="121-正向传播的计算">1.2.1. 正向传播的计算<a hidden class="anchor" aria-hidden="true" href="#121-正向传播的计算">#</a></h4>
<p>那么显而易见的，其全微分为：</p>
$$
dC = \frac{\partial f}{\partial A} dA + \frac{\partial f}{\partial B} dB
$$<p>当输入，即 $s_i$ 上一个扰动时（记作 $\dot{S_i}$），在 $C$ 上的扰动为：</p>
$$
\dot{C} = \frac{\partial f}{\partial A} \dot A + \frac{\partial f}{\partial B} \dot B
$$<h4 id="122-反向传播的计算">1.2.2. 反向传播的计算<a hidden class="anchor" aria-hidden="true" href="#122-反向传播的计算">#</a></h4>
<p>我们分析从运算链中的某一步骤向输出传到的过程：</p>
$$
s_0 = \sum C_{ij}
$$<p>那么，有：</p>
$$
ds_o = \sum \frac{\partial s_0}{\partial C_{ij}} dC_{ij} = Tr(\frac{\partial s_0}{\partial C} ^T dC)
$$<p>带入计算我们之前给出的计算链 $C = f(A, B)$，有：</p>
$$
\begin{aligned}
d(S_0) &= Tr(C^T dC)\\
dC &= \frac{\partial f}{\partial A} dA + \frac{\partial f}{\partial B} dB
\end{aligned}
$$<p>根据迹的分配律，有：</p>
$$
\begin{aligned}
d(S_0)
&= Tr(C^T, dC)  \\
&= Tr(C^T, \frac{\partial f}{\partial A}dA) + Tr(C^T,  \frac{\partial f}{\partial B} dB)\\
&= Tr(\frac{\partial f}{\partial A}^TC, dA) + Tr(\frac{\partial f}{\partial B}^T C, dB)\\
&= Tr(\frac{\partial s_o}{\partial A}, dA) + Tr(\frac{\partial s_o}{\partial B}, dB)
\end{aligned}
$$<p>这样我们可以知道：</p>
$$
\begin{aligned}
\frac{\partial s_o}{\partial A} = \frac{\partial f}{\partial A}^TC \\
\frac{\partial s_o}{\partial B} = \frac{\partial f}{\partial B}^T C
\end{aligned}
$$<h4 id="123-frobenius内积">1.2.3. Frobenius内积<a hidden class="anchor" aria-hidden="true" href="#123-frobenius内积">#</a></h4>
<p>为了更好的表达运算关系，我们定义一个二元运算 $\langle \cdot , \cdot \rangle$。这个二元组定义的本质在于矩阵的微分运算是对其中每个元素进行的，不能简单套用对矩阵全体定义的矩阵乘法运算。</p>
$$
\langle X, Y\rangle = Tr(X^T Y) = Vec(X)^T Vec(Y) \in \mathbb R
$$<p>其性质有：</p>
$$
\begin{aligned}
\langle X,Y\rangle & =\langle Y,X\rangle,  \\
\langle X,Y\rangle & =\langle X^\top,Y^\top\rangle,  \\
\langle X,YZ\rangle & =\langle Y^{\top}X,Z\rangle=\langle XZ^{\top},Y\rangle,  \\
\langle X,Y+Z\rangle & =\langle X,Y\rangle+\langle X,Z\rangle.
\end{aligned}
$$<p>用这个二元运算重新叙述上面的推导过程，可能会更加清晰。假设我们有一个函数            $f(X), X\in \mathbb R^{m\times n}$，并且 $X = AY, where. A\in \mathbb R^{m\times p},  Y\in \mathbb R^{p\times n}$，</p>
<p>我们可以把 $f$ 写作任意标量 $x$ 的梯度：
</p>
$$
\frac{\partial f}{\partial x} = \langle\frac{\partial f}{\partial X}, \frac{\partial X}{\partial x} \rangle
$$<p>我们有：</p>
$$
\partial f = \langle \frac{\partial f}{\partial X}, \partial X \rangle
$$<p>并定义：$G = \frac{\partial f}{\partial X}$，有：</p>
$$
\begin{aligned}
\frac{\partial f}{\partial x}& =\langle G,\frac{\partial(AY)}{\partial x}\rangle   \\
&=\langle G,\frac{\partial A}{\partial x}Y\rangle+\langle G,A\frac{\partial Y}{\partial x}\rangle  \\
&=\langle GY^{\top},\frac{\partial A}{\partial x}\rangle+\langle A^{\top}G,\frac{\partial Y}{\partial x}\rangle.
\end{aligned}
$$<p>对比，可归纳出：</p>
$$
\frac{\partial f}{\partial A}=GY^\top\in\mathbb{R}^{m\times p},\quad\frac{\partial f}{\partial Y}=A^\top G\in\mathbb{R}^{p\times n}.
$$<h3 id="13-常见矩阵运算的求导">1.3. 常见矩阵运算的求导<a hidden class="anchor" aria-hidden="true" href="#13-常见矩阵运算的求导">#</a></h3>
<h4 id="131-加法">1.3.1. 加法<a hidden class="anchor" aria-hidden="true" href="#131-加法">#</a></h4>
<p>假设有 $C = A+B$，那么有：</p>
$$
dC = dA + dB
$$<p>且有：</p>
$$
\begin{aligned}
ds_o
&= \langle \frac {\partial s_o}{\partial C} , dC\rangle\\
&= \langle \frac {\partial s_o}{\partial C} , dA\rangle + \langle \frac {\partial s_o}{\partial C} , dB\rangle\\
&= \langle \frac {\partial s_o}{\partial A} , dA\rangle + \langle \frac {\partial s_o}{\partial B} , dB\rangle\\
\end{aligned}
$$<p>那么：</p>
$$
\begin{aligned}
\frac{\partial s_o}{\partial A} = \frac{\partial s_o}{\partial C}\\
\frac{\partial s_o}{\partial B} = \frac{\partial s_o}{\partial C}
\end{aligned}
$$<h4 id="132-乘法">1.3.2. 乘法<a hidden class="anchor" aria-hidden="true" href="#132-乘法">#</a></h4>
<p>我们假设有：$C=AB$，那么：</p>
$$
dC = dA\; B + A\; dB
$$<p>类似的，有：</p>
$$
\begin{aligned}
ds_o
&= \langle \frac{ds_o}{dC}, dC \rangle\\
&= \langle \frac{ds_o}{dC}, dA\; B  \rangle + \langle \frac{ds_o}{dC}, A\; dB\rangle \\
&= \langle \frac{ds_o}{dC} B^T, dA  \rangle + \langle A^T \frac{ds_o}{dC}, dB\rangle \\
&= \langle \frac{\partial s_o}{\partial A}, dA\rangle + \langle \frac{\partial s_o}{\partial B}, dB\rangle
\end{aligned}
$$<p>那么，我们可以归纳出:</p>
$$
\begin{aligned}
\frac{\partial s_o}{\partial A} &= \frac{ds_o}{dC} B^T \\
\frac{\partial s_o}{\partial B} &=  A^T \frac{ds_o}{dC}
\end{aligned}
$$<h4 id="133-求逆">1.3.3. 求逆<a hidden class="anchor" aria-hidden="true" href="#133-求逆">#</a></h4>
<p>假设我们有：$C = A^{-1}$，可推得：</p>
$$
\begin{aligned}
&CA = I \\
\Longrightarrow & dC\; A + C\; dA = 0\\
\Longrightarrow & dC = -C dA C \\
\end{aligned}
$$<p>那么有正向传播：</p>
$$
\frac{d C}{d s_i} = -C \frac{d A}{d s_i} C
$$<p>类似的，反向传播有：</p>
$$
\begin{aligned}
ds_o
&= \langle \frac{ds_o}{dC}, dC \rangle\\
&= \langle \frac{ds_o}{dC}, -C dA C \rangle\\
&= \langle -C^{T} \frac{ds_o}{dC} C^T , dA\rangle\\
&= \langle \frac{ds_o}{dA} , dA\rangle
\end{aligned}
$$<p>那么有：</p>
$$
\frac{ds_o}{dA} = -C^{T} \frac{ds_o}{dC} C^T
$$<h4 id="134-二次型">1.3.4. 二次型<a hidden class="anchor" aria-hidden="true" href="#134-二次型">#</a></h4>
<p>假设：$C=  x^T A x$，那么有：</p>
$$
dC = x^TA\, dx + d(x^T A)x
$$<p>应为二次型结果是标量，那么 $d(x^T A)x$ 也应是标量，则有$(d(x^T A)x)^T = d(x^T A)x$，则有：</p>
$$
\begin{aligned}
dC
&=x^{T}Adx+x^{T}d\left(A^{T}x\right) \\
&=x^{T}Adx+x^{T}A^{T}dx \\
&=x^{T}\left(A+A^{T}\right)dx
\end{aligned}
$$<p>类似的，反向传播结果为：</p>
$$
\begin{aligned}
ds_o
&= \langle \frac{ds_o}{dC}, dC \rangle\\
&= \langle \frac{ds_o}{dC}, x^{T}(A+A^{T})dx \rangle\\
&= \langle (A+A^{T})x\frac{ds_o}{dC}, dx \rangle\\
&= \langle \frac{ds_o}{dx}, dx \rangle\\
\end{aligned}
$$<p>那么有：</p>
$$
\frac{ds_o}{dx} = (A+A^{T})x\frac{ds_o}{dC}
$$<h4 id="135-行列式">1.3.5. 行列式<a hidden class="anchor" aria-hidden="true" href="#135-行列式">#</a></h4>
<p>有：$C=det\, A$。有 $A$ 的伴随矩阵 $\tilde A$，伴随矩阵的定义为元素 $A_{ij}$ 的代数余子式，具体形式为：</p>
$$
\tilde A_{ij} = (-1)^{i+j}\det(\widehat{A}_{ij})
$$<p>根据行列式的定义，有：</p>
$$
\det A=\sum_jA_{i,j}\tilde{A}_{i,j}.
$$<p>我们固定 $i$，很容易可以得到：</p>
$$
\frac{\partial C}{\partial A_{i,j}}=\tilde{A}_{i,j}
$$<p>可推知：</p>
$$
dC = \sum_{ij} \tilde A_{ij} dA_ij
$$<p>将矩阵除法的定义 $A^{-1}=(\det A)^{-1}\tilde{A}^T$ 带入上式中，则有：</p>
$$
\begin{aligned}
dC
&= \sum_{ij} \tilde A_{ij} dA_{ij}\\
&= \sum_{ij} A^{-1}_{ij} \det A dA_{ij}\\
&= \det A\sum_{ij} A^{-1}_{ij}  dA_{ij}\\
&= C\, Tr(A^{-1} dA)\\
\end{aligned}
$$<p>反向梯度有：</p>
$$
\begin{aligned}
ds_o
&= \langle \frac{ds_o}{dC}, dC \rangle\\
&= \langle \frac{ds_o}{dC}, C\, Tr(A^{-1} dA) \rangle\\
&= \langle \frac{ds_o}{dC} C A^{-T}, dA \rangle\\
&= \langle \frac{ds_o}{dA}, dA \rangle\\
\end{aligned}
$$<p>那么有：</p>
$$
\frac{ds_o}{dA} = \frac{ds_o}{dC} C A^{-T}
$$<h2 id="2-3dgs-中光栅化的正向和反向过程">2. 3DGS 中光栅化的正向和反向过程<a hidden class="anchor" aria-hidden="true" href="#2-3dgs-中光栅化的正向和反向过程">#</a></h2>
<h3 id="21-深度合成过程">2.1. 深度合成过程<a hidden class="anchor" aria-hidden="true" href="#21-深度合成过程">#</a></h3>
<h4 id="211-正向过程">2.1.1. 正向过程<a hidden class="anchor" aria-hidden="true" href="#211-正向过程">#</a></h4>
<p>此处的正向过程是指已经投影在二维像素平面上的二维高斯函数合成的过程。我们不加说明的给出合成的形式，具体推导可以参考上一篇文章。</p>
$$
\begin{aligned}
C_i = \sum_{n\leq N} c_n \cdot \alpha_n \cdot T_n\\
\text{where. } T_n = \prod_{m<n} (1 - \alpha_m)
\end{aligned}
$$<p>我们对不透明度和色彩进行这样的处理：</p>
$$
\alpha_n=o_n\cdot\exp(-\sigma_n),
\quad\sigma_n=\frac{1}{2}\Delta_n^\top\Sigma^{\prime-1}\Delta_n,
$$<p>其中 $\Delta_n$ 是该位置距像素中心的距离。</p>
<h4 id="212-反向过程">2.1.2. 反向过程<a hidden class="anchor" aria-hidden="true" href="#212-反向过程">#</a></h4>
<p>首先我们推导由像素 $i$ 产生的损失梯度对高斯函数 $n$ 产生的影响。我们需要计算出<strong>投影色彩残差</strong>$\frac{\partial \mathcal L}{\partial C_i}$对<strong>色彩梯度</strong> $\frac{\partial \mathcal L}{\partial c_n}$，<strong>不透明度</strong> $\frac{\partial \mathcal L}{\partial o_n}$ 和<strong>2D高斯函数均值和方差</strong>$\frac{\partial \mathcal L}{\partial \mu_n}，\frac{\partial \mathcal L}{\partial \Sigma'_n}$。</p>
<p>在正向过程中，我们计算出每个高斯函数对该像素色彩的贡献，并通过体渲染的方式叠加在一起。而在反向传播时，则是从投影的色彩残差到高斯函数属性的梯度。</p>
<ul>
<li>对于$\frac{\partial \mathcal L}{\partial c_n}$，我们可以很容易写出，其中 $k$ 为色彩的通道数</li>
</ul>
$$
\frac{\partial C_i(k)}{\partial c_n(k)} = \alpha_n T_n
$$<p>而 $T_n$ 通过递推来计算：</p>
$$
T_{n-1}=\frac{T_n}{1-\alpha_{n-1}}.
$$<ul>
<li>对于 $\frac{\partial \mathcal L}{\partial o_n}$，有：</li>
</ul>
$$
\frac{\partial C_i(k)}{\partial\alpha_n}=c_n(k)\cdot T_n-\frac{S_n(k)}{1-\alpha_n}\mathrm{where.} \,S_n=\sum_{m>n}c_m\alpha_mT_m.
$$<p>同样的 $S_n$ 可以通过递归来计算：</p>
$$
\begin{aligned}
S_{N}(k)& =0  \\
S_{n-1}(k)& =c_{n}(k)\alpha_{n}T_{n}+S_{n}(k).
\end{aligned}
$$<ul>
<li>对于不透明度 $\frac{\partial \alpha_n}{\partial o_n}$，有：</li>
</ul>
$$
\frac{\partial\alpha_n}{\partial o_n}=\exp(-\sigma_n),\quad\frac{\partial\alpha_n}{\partial\sigma_n}=-o_n\exp(-\sigma_n).
$$<ul>
<li>对于二维均值 $\frac{\partial\sigma_n}{\partial\mu_n'}$</li>
</ul>
$$
\frac{\partial\sigma_n}{\partial\mu_n'}=\frac{\partial\sigma_n}{\partial\Delta_n}=\Sigma_n'^{-1}\Delta_n\in\mathbb{R}^2.
$$<ul>
<li>对于二维协方差，我们令$Y=\Sigma'^{-1}_n$，有$\frac{\partial \sigma_n}{\partial o_n}$</li>
</ul>
$$
\frac{\partial\sigma_n}{\partial Y}=\frac12\Delta_n\Delta_n^\top\in\mathbb{R}^{2\times2}.
$$<p>我们运用之前推导的矩阵的逆的求导规则，则有：</p>
$$
\begin{aligned}
\frac{\partial\sigma_{n}}{\partial x}& =\langle G,-Y\frac{\partial\Sigma_{n}^{\prime}}{\partial x}Y\rangle   \\
&=\langle-Y^{\top}GY^{\top},\frac{\partial\Sigma_{n}^{\prime}}{\partial x}\rangle
\end{aligned}
$$<p>带入到前面的推导中，则有：</p>
$$
\frac{\partial\sigma_n}{\partial\Sigma_n^{\prime}}=-\frac{1}{2}\Sigma_n^{\prime-1}\Delta_n\Delta_n^\top\Sigma_n^{\prime-1}.
$$<h3 id="22-投影过程">2.2. 投影过程<a hidden class="anchor" aria-hidden="true" href="#22-投影过程">#</a></h3>
<h4 id="221-正向过程">2.2.1. 正向过程<a hidden class="anchor" aria-hidden="true" href="#221-正向过程">#</a></h4>
<p>我们假设相机的外参$T_{cw}$，投影矩阵为$P$，有：
</p>
$$
T_{\text{cw}}=\begin{bmatrix}R_{\text{cw}}&t_{\text{cw}}\\0&1\end{bmatrix}\in SE(3),\quad P=\begin{bmatrix}2f_x/w&0&0&0\\0&2f_y/h&0&0\\0&0&(f+n)/(f-n)&-2fn/(f-n)\\0&0&1&0\end{bmatrix}
$$<p>
这里的投影矩阵有些讲究，通过第三行第四行两个形式将原始的深度限制在来 $[f,n]$之间。我们先通过 $T_{cw}$ 将世界坐标系下的三维高斯函数均值变换到相机坐标系下，后再逐步将其投影到像素坐标系下：
</p>
$$
t=T_{\text{cw}}\begin{bmatrix}\mu&1\end{bmatrix}^\top,\quad t'=Pt,\quad\mu'=\begin{bmatrix}(w\cdot t_x'/t_w'+1)/2+c_x\\(h\cdot t_y'/t_w'+1)/2+c_y\end{bmatrix},
$$<p>
对于协方差，还需计算仿射变换 $J$。
</p>
$$
J=\begin{bmatrix}f_x/t_z&0&-f_x\cdot t_x/t_z^2\\0&f_y/t_z&-f_y\cdot t_y/t_z^2\end{bmatrix}
$$<p>
投影的 $2D$ 高斯函数协方差为：
</p>
$$
\Sigma^{\prime}=JR_\text{cw}{\Sigma}R_\text{cw}^{\top}J^{\top}
$$<p>
此外，我们将 $3D$ 协方差表达为一个表达旋转的四元数 $q\in \mathbb R^4$和缩放 $s$，我们假设 $q=(x,y,z,w)$，那么这个旋转矩阵可以写成：
</p>
$$
R=\begin{bmatrix}1-2\cdot(y^2+z^2)&2\cdot(xy-wz)&2\cdot(xz+wy)\\2\cdot(xy+wz)&1-2\cdot(x^2-z^2)&2\cdot(yz-wx)\\2\cdot(xz-wy)&2\cdot(yz+wx)&1-2\cdot(x^2+y^2)\end{bmatrix}
$$<p>那么三维高斯函数的协方差可以写作：
</p>
$$
\Sigma=RSS^\top R^\top
$$<h4 id="222-反向过程">2.2.2. 反向过程<a hidden class="anchor" aria-hidden="true" href="#222-反向过程">#</a></h4>
<p>在上一小节中，我们推导了颜色残差与二维高斯函数参数梯度之间的关系，我们可以继续将其反向传播到三维高斯函数上。我们首先计算二维均值 $\mu'$ 对相机坐标 $t$ 的梯度贡献，并将二维协方差 $\Sigma'$ 计算为三维协方差 $\Sigma$ 和相机坐标 $t$​​。
</p>
$$
\frac{\partial\mathcal{L}}{\partial t_i}=\frac{\partial\mathcal{L}_{\mu^{\prime}}}{\partial t_i}+\frac{\partial\mathcal{L}_{\Sigma^{\prime}}}{\partial t_i}=\frac{\partial\mathcal{L}}{\partial\mu^{\prime}}\frac{\partial\mu^{\prime}}{\partial t_i}+\langle\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}},\frac{\partial\Sigma^{\prime}}{\partial t_i}\rangle
$$<p>
其中二维协方差 $\Sigma'$ 对三维协方差 $\Sigma$ 和相机位置 $t$ 有贡献。我们有：
</p>
$$
\Sigma^{\prime}=T\Sigma T^\top
$$<p>
求其全微分，有：
</p>
$$
\partial \Sigma' = (\partial T)\Sigma T^T + T(\partial \Sigma) T^T + T\Sigma (\partial T^T)
$$<p>
我们令 $G=\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}$，并带入上式有：
</p>
$$
\begin{aligned}
\partial\mathcal{L}_{\Sigma^{\prime}}& =\langle G,\partial\Sigma'\rangle   \\
&=\langle G,(\partial T)\Sigma T^\top+T(\partial\Sigma)T^\top+T\Sigma(\partial T^\top)\rangle  \\
&=\langle GT\Sigma^\top,\partial T\rangle+\langle T^\top GT,\partial\Sigma\rangle+\langle G^\top T\Sigma,\partial T\rangle  \\
&=\langle GT\Sigma^\top+G^\top T\Sigma,\partial T\rangle+\langle T^\top GT,\partial\Sigma\rangle.
\end{aligned}
$$<p>
容易得到：
</p>
$$
\begin{aligned}
\frac{\partial\mathcal{L}}{\partial\Sigma}&=T^\top\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}T.\\
\frac{\partial\mathcal{L}}{\partial T}&=\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}T\Sigma^\top+\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}^\top T\Sigma
\end{aligned}
$$<p>
我们通过 $T=JR_{\mathrm{cw}}$ 将梯度传播到 $J$
</p>
$$
\begin{aligned}
\partial\mathcal{L}
&=\langle\frac{\partial\mathcal{L}}{\partial T},(\partial J)R_{\mathrm{cw}}\rangle=\langle\frac{\partial\mathcal{L}}{\partial T}R_{\mathrm{cw}}^\top,\partial J\rangle\\
&= \langle (\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}T\Sigma^\top+\frac{\partial\mathcal{L}}{\partial\Sigma^{\prime}}^\top T\Sigma)R_{\mathrm{cw}}^\top,\partial J\rangle
\end{aligned}
$$<p>下面将梯度从 $J$ 传播到相机坐标 $t$，我们通过计算 $\Sigma'$ 对 $t$ 梯度的贡献：</p>
$$
\begin{aligned}
\frac{\partial J}{\partial t_x}
&=\begin{bmatrix}0&0&-f_x/t_z^2\\0&0&0\end{bmatrix}
\\\quad\frac{\partial J}{\partial t_y}&
=\begin{bmatrix}0&0&0\\0&0&-f_y/t_z^2\end{bmatrix}
\\\quad\frac{\partial J}{\partial t_z}
&=\begin{bmatrix}-f_x/t_z^2&0&2f_xt_x/t_z^3\\0&-f_y/t_z^2&2f_yt_y/t_z^3\end{bmatrix}
\\\quad\frac{\partial J}{\partial t_w}
&=\mathbf{0}^{2\times3}
\end{aligned}
$$<p>根据式（58）的形式，我们需要将 $\frac{\partial\mathcal{L}_{\mu^{\prime}}}{\partial t_i}$ 和 $\frac{\partial\mathcal{L}_{\Sigma^{\prime}}}{\partial t_i}$ 相加构成 $\frac{\partial L}{\partial t}$，并将这个结果传导到三维高斯函数的均值 $\mu$ 和$T_{cw}$上，其中 $q = [\mu, 1]^T$：</p>
$$
\begin{aligned}
\partial\mathcal{L}& =\langle G,\partial t\rangle=\langle G,\partial(T_\text{cw}q)\rangle   \\
&=\langle Gq^\top,\partial T_{\mathrm{cw}}\rangle+\langle T_{\mathrm{cw}}^\top G,\partial q\rangle.
\end{aligned}
$$<p>易得到：</p>
$$
\begin{aligned}
\frac{\partial\mathcal{L}}{\partial T_{\mathrm{cw}}}
&=\frac{\partial\mathcal{L}}{\partial t}q^\top\\
\quad\frac{\partial\mathcal{L}} {\partial\mu}
&=R_{\mathrm{cw}}^\top\begin{bmatrix}\frac{\partial\mathcal{L}}{\partial t_x}&\frac{\partial\mathcal{L}}{\partial t_y}&\frac{\partial\mathcal{L}}{\partial t_z}\end{bmatrix}^\top\in\mathbb{R}^3
\end{aligned}
$$<p>最后推导三维协方差到旋转 $q$ 和缩放 $s$ 的梯度：</p>
<p>我们令 $M=RS$，则有：$\Sigma = MM^T$，令$G=\frac{\partial\mathcal{L}}{\partial\Sigma}$，则有：</p>
$$
\begin{aligned}
\partial\mathcal{L}& =\langle G,\partial\Sigma\rangle   \\
&\begin{aligned}&=\langle G,(\partial M)M^\top+M(\partial M^\top)\rangle\end{aligned} \\
&=\langle GM+G^\top M,\partial M\rangle
\end{aligned}
$$<p>那么有：</p>
$$
\frac{\partial\mathcal{L}}{\partial M}=\frac{\partial\mathcal{L}}{\partial\Sigma}M+\frac{\partial\mathcal{L}}{\partial\Sigma}^\top M.
$$<p>类似的，应为 $M=RS$，有：</p>
$$
\begin{aligned}
\partial\mathcal{L}& =\langle G,\partial M\rangle   \\
&=\langle G,(\partial R)S\rangle+\langle G,R(\partial S)\rangle  \\
&\begin{aligned}&=\langle GS^\top,\partial R\rangle+\langle R^\top G,\partial S\rangle\end{aligned}
\end{aligned}
$$<p>有：</p>
$$
\frac{\partial\mathcal{L}}{\partial R}=\frac{\partial L}{\partial M}S^\top,\quad\frac{\partial\mathcal{L}}{\partial S}=R^\top\frac{\partial L}{\partial M}
$$<p>最后，再将旋转矩阵的梯度传导到四元数上：</p>
$$
\begin{aligned}
\frac{\partial R}{\partial w}=2\begin{bmatrix}0&-z&y\\z&0&-x\\-y&x&0\end{bmatrix},
\\\quad\frac{\partial R}{\partial x}=2\begin{bmatrix}0&y&z\\y&-2x&-w\\z&w&-2x\end{bmatrix},
\\\quad\frac{\partial R}{\partial y}=2\begin{bmatrix}-2y&x&w\\x&0&z\\-w&z&-2y\end{bmatrix},
\\\quad\frac{\partial R}{\partial z}=2\begin{bmatrix}-2z&-w&x\\w&-2z&y\\x&y&0\end{bmatrix}
\end{aligned}
$$

  </div>

  <footer class="post-footer">
    <ul class="post-tags">
      <li><a href="https://wangjv0812.github.io/WangJV-Blog-Pages/tags/3d-gaussian-splatting/">3D Gaussian Splatting</a></li>
      <li><a href="https://wangjv0812.github.io/WangJV-Blog-Pages/tags/%E4%BD%93%E6%B8%B2%E6%9F%93/">体渲染</a></li>
      <li><a href="https://wangjv0812.github.io/WangJV-Blog-Pages/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9B%BE%E5%BD%A2%E5%AD%A6/">计算机图形学</a></li>
      <li><a href="https://wangjv0812.github.io/WangJV-Blog-Pages/tags/%E6%95%B0%E5%AD%A6/">数学</a></li>
    </ul>
<nav class="paginav">
  <a class="prev" href="https://wangjv0812.github.io/WangJV-Blog-Pages/2024/07/hierarchical-gaussian-splatting/">
    <span class="title">« Prev</span>
    <br>
    <span>Hierarchical Gaussian Splatting</span>
  </a>
  <a class="next" href="https://wangjv0812.github.io/WangJV-Blog-Pages/2024/05/mathematics-in-3dgs-1/">
    <span class="title">Next »</span>
    <br>
    <span>Mathematics In 3DGS 1</span>
  </a>
</nav>

  </footer>
</article>
    </main>
    
<footer class="footer">
        <span>&copy; 2025 <a href="https://wangjv0812.github.io/WangJV-Blog-Pages/">WangJV Blog</a></span> · 

    <span>
        Powered by
        <a href="https://gohugo.io/" rel="noopener noreferrer" target="_blank">Hugo</a> &
        <a href="https://github.com/adityatelange/hugo-PaperMod/" rel="noopener" target="_blank">PaperMod</a>
    </span>
</footer>
<a href="#top" aria-label="go to top" title="Go to Top (Alt + G)" class="top-link" id="top-link" accesskey="g">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 6" fill="currentColor">
        <path d="M12 6H0l6-6z" />
    </svg>
</a>

<script>
    let menu = document.getElementById('menu')
    if (menu) {
        menu.scrollLeft = localStorage.getItem("menu-scroll-position");
        menu.onscroll = function () {
            localStorage.setItem("menu-scroll-position", menu.scrollLeft);
        }
    }

    document.querySelectorAll('a[href^="#"]').forEach(anchor => {
        anchor.addEventListener("click", function (e) {
            e.preventDefault();
            var id = this.getAttribute("href").substr(1);
            if (!window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView({
                    behavior: "smooth"
                });
            } else {
                document.querySelector(`[id='${decodeURIComponent(id)}']`).scrollIntoView();
            }
            if (id === "top") {
                history.replaceState(null, null, " ");
            } else {
                history.pushState(null, null, `#${id}`);
            }
        });
    });

</script>
<script>
    var mybutton = document.getElementById("top-link");
    window.onscroll = function () {
        if (document.body.scrollTop > 800 || document.documentElement.scrollTop > 800) {
            mybutton.style.visibility = "visible";
            mybutton.style.opacity = "1";
        } else {
            mybutton.style.visibility = "hidden";
            mybutton.style.opacity = "0";
        }
    };

</script>
<script>
    document.getElementById("theme-toggle").addEventListener("click", () => {
        if (document.body.className.includes("dark")) {
            document.body.classList.remove('dark');
            localStorage.setItem("pref-theme", 'light');
        } else {
            document.body.classList.add('dark');
            localStorage.setItem("pref-theme", 'dark');
        }
    })

</script>
<script>
    document.querySelectorAll('pre > code').forEach((codeblock) => {
        const container = codeblock.parentNode.parentNode;

        const copybutton = document.createElement('button');
        copybutton.classList.add('copy-code');
        copybutton.innerHTML = 'copy';

        function copyingDone() {
            copybutton.innerHTML = 'copied!';
            setTimeout(() => {
                copybutton.innerHTML = 'copy';
            }, 2000);
        }

        copybutton.addEventListener('click', (cb) => {
            if ('clipboard' in navigator) {
                navigator.clipboard.writeText(codeblock.textContent);
                copyingDone();
                return;
            }

            const range = document.createRange();
            range.selectNodeContents(codeblock);
            const selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
            try {
                document.execCommand('copy');
                copyingDone();
            } catch (e) { };
            selection.removeRange(range);
        });

        if (container.classList.contains("highlight")) {
            container.appendChild(copybutton);
        } else if (container.parentNode.firstChild == container) {
            
        } else if (codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.nodeName == "TABLE") {
            
            codeblock.parentNode.parentNode.parentNode.parentNode.parentNode.appendChild(copybutton);
        } else {
            
            codeblock.parentNode.appendChild(copybutton);
        }
    });
</script>
</body>

</html>
